﻿using System;
using System.Collections.Generic;

using System.Reflection;
using RoughJs.E4X;

namespace RoughJs.Ast
{
    /// <summary>
    /// 可以脚本化的对象
    /// </summary>
    public abstract class ScriptableObject : Scriptable, DebuggableObject, ConstProperties
    {
        #region 常量
        /// <summary>
        /// 设置空属性
        /// </summary>
        public const int EMPTY = 0x00;
        /// <summary>
        /// 只读属性
        /// </summary>
        public const int READONLY = 0x01;
        /// <summary>
        /// 属性不能被枚举
        /// </summary>
        public const int DONTENUM = 0x02;

        /// <summary>
        /// 属性不能被删除
        /// </summary>
        public const int PERMANENT = 0x04;

        /// <summary>
        /// 属性是一个常量属性
        /// </summary>
        public const int UNINITIALIZED_CONST = 0x08;
        /// <summary>
        /// 常量
        /// </summary>
        public const int CONST = PERMANENT | READONLY | UNINITIALIZED_CONST;


        /// <summary>
        /// 如果值没有找到，就从get中返回
        /// </summary>
        public static Object NOT_FOUND = UniqueTag.NOT_FOUND;



        #endregion

        /// <summary>
        /// 对象的原生类型
        /// </summary>
        private Scriptable prototypeObject;
        /// <summary>
        /// 这个对象的父域
        /// </summary>
        private Scriptable parentScopeObject;


        private Slot[] slots;
        // If count >= 0, it gives number of keys or if count < 0,
        // it indicates sealed object where ~count gives number of keys
        private int count;

        // gateways into the definition-order linked list of slots
        private Slot firstAdded;
        private Slot lastAdded;

        // cache; may be removed for smaller memory footprint
        private Slot lastAccess = REMOVED;

        private static Slot REMOVED = new Slot(null, 0, READONLY);

        private volatile IDictionary<Object, Object> associatedValues;

        private static int SLOT_QUERY = 1;
        private static int SLOT_MODIFY = 2;
        private static int SLOT_REMOVE = 3;
        private static int SLOT_MODIFY_GETTER_SETTER = 4;
        private static int SLOT_MODIFY_CONST = 5;

        //  REMOVED.wasDeleted = true;

        #region ConstProperties Members
        /// <summary>
        /// 设置const属性
        /// </summary>
        /// <param name="name"></param>
        /// <param name="start"></param>
        /// <param name="value"></param>
        public virtual void putConst(string name, Scriptable start, object value)
        {
            if (putImpl(name, 0, start, value, READONLY))
                return;

            if (start == this) throw Kit.codeBug();
            if (start is ConstProperties)
                ((ConstProperties)start).putConst(name, start, value);
            else
                start.put(name, start, value);
        }

        public virtual void defineConst(string propertyName, Scriptable destination)
        {
            if (destination is ConstProperties)
            {
                ConstProperties cp = (ConstProperties)destination;
                cp.defineConst(propertyName, destination);
            }
            else
                defineProperty(destination, propertyName, Undefined.instance, CONST);
        }

        public bool isConst(string name)
        {
            Slot slot = getSlot(name, 0, SLOT_QUERY);
            if (slot == null)
            {
                return false;
            }
            return (slot.getAttributes() & (PERMANENT | READONLY)) ==
                                           (PERMANENT | READONLY);
        }

        #endregion

        #region DebuggableObject Members

        public virtual object[] getAllIds()
        {
            return getIds(true);
        }

        #endregion

        #region Scriptable Members

        public abstract string getClassName();

        public virtual object get(string name, Scriptable start) { return getImpl(name, 0, start); }

        public virtual object get(int index, Scriptable start) { return getImpl(null, index, start); }

        public virtual bool has(string name, Scriptable start)
        {
            return null != getSlot(name, 0, SLOT_QUERY);
        }

        public virtual bool has(int index, Scriptable start) { return null != getSlot(null, index, SLOT_QUERY); }

        public virtual void put(string name, Scriptable start, object value)
        {

            if (putImpl(name, 0, start, value, EMPTY))
                return;

            if (start == this) throw Kit.codeBug();
            start.put(name, start, value);

        }
        public virtual void put(int index, Scriptable start, object value)
        {

            if (putImpl(null, index, start, value, EMPTY))
                return;

            if (start == this) throw Kit.codeBug();
            start.put(index, start, value);
        }

        public virtual void delete(string name)
        {
            checkNotSealed(name, 0);
            accessSlot(name, 0, SLOT_REMOVE);
        }
        public virtual void delete(int index)
        {
            checkNotSealed(null, index);
            accessSlot(null, index, SLOT_REMOVE);
        }

        /// <summary>
        /// 得到prototype
        /// </summary>
        /// <returns></returns>
        public virtual Scriptable getPrototype()
        {
            return prototypeObject;
        }

        /// <summary>
        /// 设置prototype
        /// </summary>
        /// <param name="prototype"></param>
        public virtual void setPrototype(Scriptable prototype) { prototypeObject = prototype; }

        /// <summary>
        /// 返回对象包括的类    
        /// </summary>
        /// <returns></returns>
        public virtual Scriptable getParentScope() { return parentScopeObject; }

        /// <summary>
        /// 返回对象包括的类
        /// </summary>
        /// <param name="parent"></param>
        public virtual void setParentScope(Scriptable parent) { parentScopeObject = parent; }

        /// <summary>
        /// 返回对象属性的id数组
        /// </summary>
        /// <returns></returns>
        public virtual object[] getIds()
        {
            return getIds(false);
        }

        public virtual Object[] getIds(bool getAll)
        {
            Slot[] s = slots;
            Object[] a = ScriptRuntime.emptyArgs;
            if (s == null)
                return a;
            int c = 0;
            Slot slot = firstAdded;
            while (slot != null && slot.wasDeleted)
            {
                // as long as we're traversing the order-added linked list,
                // remove deleted slots
                slot = slot.orderedNext;
            }
            firstAdded = slot;
            if (slot != null)
            {
                for (; ; )
                {
                    if (getAll || (slot.getAttributes() & DONTENUM) == 0)
                    {
                        if (c == 0)
                            a = new Object[s.Length];
                        a[c++] = slot.name != null
                                     ? (Object)slot.name
                                     : slot.indexOrHash;
                    }
                    Slot next = slot.orderedNext;
                    while (next != null && next.wasDeleted)
                    {
                        // remove deleted slots
                        next = next.orderedNext;
                    }
                    slot.orderedNext = next;
                    if (next == null)
                    {
                        break;
                    }
                    slot = next;
                }
            }
            lastAdded = slot;
            if (c == a.Length)
                return a;
            Object[] result = new Object[c];
            Array.Copy(a, 0, result, 0, c);
            return result;
        }


        /// <summary>
        /// 实现内部方法的 默认值
        /// 参看 ECMA 8.6.2.6.
        /// </summary>
        /// <param name="hint"></param>
        /// <returns></returns>
        public virtual object getDefaultValue(Type hint)
        {
            return getDefaultValue(this, hint);
        }

        public static Object getDefaultValue(Scriptable object1, Type typeHint)
        {
            Context cx = null;
            for (int i = 0; i < 2; i++)
            {
                bool tryToString;
                if (typeHint == ScriptRuntime.StringClass)
                {
                    tryToString = (i == 0);
                }
                else
                {
                    tryToString = (i == 1);
                }

                String methodName;
                Object[] args;
                if (tryToString)
                {
                    methodName = "toString";
                    args = ScriptRuntime.emptyArgs;
                }
                else
                {
                    methodName = "valueOf";
                    args = new Object[1];
                    String hint;
                    if (typeHint == null)
                    {
                        hint = "undefined";
                    }
                    else if (typeHint == ScriptRuntime.StringClass)
                    {
                        hint = "string";
                    }
                    else if (typeHint == ScriptRuntime.ScriptableClass)
                    {
                        hint = "object";
                    }
                    else if (typeHint == ScriptRuntime.FunctionClass)
                    {
                        hint = "function";
                    }
                    else if (typeHint == ScriptRuntime.BooleanClass
                             || typeHint == typeof(Boolean))
                    {
                        hint = "boolean";
                    }
                    else if (typeHint == ScriptRuntime.NumberClass ||
                           typeHint == ScriptRuntime.ByteClass ||
                           typeHint == typeof(Byte) ||
                           typeHint == ScriptRuntime.ShortClass ||
                           typeHint == typeof(Int16) ||
                           typeHint == ScriptRuntime.IntegerClass ||
                           typeHint == typeof(Int32) ||
                           typeHint == ScriptRuntime.FloatClass ||
                           typeHint == typeof(Single) ||
                           typeHint == ScriptRuntime.DoubleClass ||
                           typeHint == typeof(Double))
                    {
                        hint = "number";
                    }
                    else
                    {
                        throw Context.reportRuntimeError1(
                            "msg.invalid.type", typeHint.ToString());
                    }
                    args[0] = hint;
                }
                Object v = getProperty(object1, methodName);
                if (!(v is Function))
                    continue;
                Function fun = (Function)v;
                if (cx == null)
                    cx = Context.getContext();
                v = fun.call(cx, fun.getParentScope(), object1, args);
                if (v != null)
                {
                    if (!(v is Scriptable))
                    {
                        return v;
                    }
                    if (typeHint == ScriptRuntime.ScriptableClass
                        || typeHint == ScriptRuntime.FunctionClass)
                    {
                        return v;
                    }
                    if (tryToString && v is Wrapper)
                    {
                        // Let a wrapped java.lang.String pass for a primitive
                        // string.
                        Object u = ((Wrapper)v).unwrap();
                        if (u is String)
                            return u;
                    }
                }
            }
            // fall through to error
            String arg = (typeHint == null) ? "undefined" : typeHint.Name;
            throw ScriptRuntime.typeError1("msg.default.value", arg);
        }

        public virtual bool hasInstance(Scriptable instance)
        {
            // Default for JS objects (other than Function) is to do prototype
            // chasing.  This will be overridden in NativeFunction and non-JS
            // objects.

            return ScriptRuntime.jsDelegatesTo(instance, this);
        }


        #endregion

        public Object getGetterOrSetter(String name, int index, bool isSetter)
        {
            if (name != null && index != 0)
                throw new ArgumentException(name);
            Slot slot = getSlot(name, index, SLOT_QUERY);
            if (slot == null)
                return null;
            if (slot is GetterSlot)
            {
                GetterSlot gslot = (GetterSlot)slot;
                Object result = isSetter ? gslot.setter : gslot.getter;
                return result != null ? result : Undefined.instance;
            }
            else
                return Undefined.instance;
        }

        public void setGetterOrSetter(String name, int index,
                                  Callable getterOrSetter, bool isSetter)
        {
            if (name != null && index != 0)
                throw new ArgumentException(name);

            checkNotSealed(name, index);
            GetterSlot gslot = (GetterSlot)getSlot(name, index,
                                                   SLOT_MODIFY_GETTER_SETTER);
            gslot.checkNotReadonly();
            if (isSetter)
            {
                gslot.setter = getterOrSetter;
            }
            else
            {
                gslot.getter = getterOrSetter;
            }
            gslot.value = Undefined.instance;
        }

        /// <summary>
        /// 返回是否对象或者对象的prototype链存在属性
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="name">属性名</param>
        /// <returns></returns>
        public static bool hasProperty(Scriptable obj, String name)
        {
            return null != getBase(obj, name);
        }
        /// <summary>
        /// 返回是否对象或者对象的prototype链存在属性
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="index">索引</param>
        /// <returns></returns>
        public static bool hasProperty(Scriptable obj, int index)
        {
            return null != getBase(obj, index);
        }

        /// <summary>
        /// 从任意一个对象或者对象的prototyp链中得到属性
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="name">属性名</param>
        /// <returns></returns>
        public static Object getProperty(Scriptable obj, String name)
        {
            Scriptable start = obj;
            Object result;
            do
            {
                result = obj.get(name, start);//得到属性
                if (result != ScriptableObject.NOT_FOUND)
                    break;
                obj = obj.getPrototype();
            } while (obj != null);
            return result;
        }



        /// <summary>
        /// 从任意一个对象或者对象的prototyp链中得到属性
        /// </summary>
        /// <param name="obj">对象</param>
        /// <param name="index">索引</param>
        /// <returns></returns>
        public static Object getProperty(Scriptable obj, int index)
        {
            Scriptable start = obj;
            Object result;
            do
            {
                result = obj.get(index, start);
                if (result != ScriptableObject.NOT_FOUND)
                    break;
                obj = obj.getPrototype();
            } while (obj != null);
            return result;
        }


        /// <summary>
        /// 设置属性
        /// </summary>
        /// <param name="name"></param>
        /// <param name="attributes"></param>
        public virtual void setAttributes(String name, int attributes)
        {
            checkNotSealed(name, 0);
            findAttributeSlot(name, 0, SLOT_MODIFY).setAttributes(attributes);
        }

        /// <summary>
        /// 定义javascript属性
        /// </summary>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        /// <param name="attributes"></param>
        public void defineProperty(String propertyName, Object value,
                               int attributes)
        {
            checkNotSealed(propertyName, 0);
            put(propertyName, this, value);
            setAttributes(propertyName, attributes);
        }

        /// <summary>
        /// 设置属性值在一个属性中
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public static void putProperty(Scriptable obj, String name, Object value)
        {
            Scriptable base1 = getBase(obj, name);
            if (base1 == null)
                base1 = obj;
            base1.put(name, obj, value);
        }

        /// <summary>
        /// 设置属性值在一个属性中
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="index"></param>
        /// <param name="value"></param>
        public static void putProperty(Scriptable obj, int index, Object value)
        {
            Scriptable base1 = getBase(obj, index);
            if (base1 == null)
                base1 = obj;
            base1.put(index, obj, value);
        }

        /// <summary>
        /// 实用的方法将属性添加到任意的脚本化的对象。
        ///  如果目的地是ScriptableObject实例，调用
        ///  defineProperty那里，否则要求把目的地
        /// 无视属性
        /// </summary>
        /// <param name="destination"></param>
        /// <param name="propertyName"></param>
        /// <param name="value"></param>
        /// <param name="attributes"></param>
        public static void defineProperty(Scriptable destination,
                                      String propertyName, Object value,
                                      int attributes)
        {
            if (!(destination is ScriptableObject))
            {
                destination.put(propertyName, destination, value);
                return;
            }
            ScriptableObject so = (ScriptableObject)destination;
            so.defineProperty(propertyName, value, attributes);
        }

        public static bool deleteProperty(Scriptable obj, int index)
        {
            Scriptable base1 = getBase(obj, index);
            if (base1 == null)
                return true;
            base1.delete(index);
            return !base1.has(index, obj);
        }

        public static bool deleteProperty(Scriptable obj, String name)
        {
            Scriptable base1 = getBase(obj, name);
            if (base1 == null)
                return true;
            base1.delete(name);
            return !base1.has(name, obj);
        }

        //     static Method findSingleMethod(Method[] methods, String name)
        //{
        //    Method found = null;
        //    for (int i = 0, N = methods.length; i != N; ++i) {
        //        Method method = methods[i];
        //        if (method != null && name.equals(method.getName())) {
        //            if (found != null) {
        //                throw Context.reportRuntimeError2(
        //                    "msg.no.overload", name,
        //                    method.getDeclaringClass().getName());
        //            }
        //            found = method;
        //        }
        //    }
        //    return found;
        //}

        public void defineFunctionProperties(String[] names, Type clazz,
                                         int attributes)
        {
            MethodInfo[] methods = FunctionObject.getMethodList(clazz);
            for (int i = 0; i < names.Length; i++)
            {
                String name = names[i];
                MethodInfo m = FunctionObject.findSingleMethod(methods, name);
                if (m == null)
                {
                    throw Context.reportRuntimeError2(
                        "msg.method.not.found", name, clazz.Name);
                }
                FunctionObject f = new FunctionObject(name, m, this);
                defineProperty(name, f, attributes);
            }
        }

        private static Scriptable getBase(Scriptable obj, String name)
        {
            do
            {
                if (obj.has(name, obj))
                    break;
                obj = obj.getPrototype();
            } while (obj != null);
            return obj;
        }

        private static Scriptable getBase(Scriptable obj, int index)
        {
            do
            {
                if (obj.has(index, obj))
                    break;
                obj = obj.getPrototype();
            } while (obj != null);
            return obj;
        }


        /// <summary>
        /// 得到对象的prototype
        /// 参见 ECMA 15.2.4.
        /// </summary>
        /// <param name="scope"></param>
        /// <returns></returns>
        public static Scriptable getObjectPrototype(Scriptable scope)
        {
            return getClassPrototype(scope, "Object");
        }

        /// <summary>
        /// 得到方法的prototype 
        /// 参见 ECMA 15.3.4.
        /// </summary>
        /// <param name="scope"></param>
        /// <returns></returns>
        public static Scriptable getFunctionPrototype(Scriptable scope)
        {
            return getClassPrototype(scope, "Function");
        }


        /// <summary>
        /// 得到类的prototype
        /// </summary>
        /// <param name="scope"></param>
        /// <param name="className"></param>
        /// <returns></returns>
        public static Scriptable getClassPrototype(Scriptable scope,
                                                   String className)
        {
            scope = getTopLevelScope(scope);
            Object ctor = getProperty(scope, className);
            Object proto;
            if (ctor is BaseFunction)
            {
                proto = ((BaseFunction)ctor).getPrototypeProperty();
            }
            else if (ctor is Scriptable)
            {
                Scriptable ctorObj = (Scriptable)ctor;
                proto = ctorObj.get("prototype", ctorObj);
            }
            else
            {
                return null;
            }
            if (proto is Scriptable)
            {
                return (Scriptable)proto;
            }
            return null;
        }

        public class Slot
        {
            private volatile short attributes;
            public String name; // This can change due to caching
            public int indexOrHash;

            public volatile bool wasDeleted;
            public volatile Object value;
            public volatile Slot next; // next in hash table bucket
            public volatile Slot orderedNext; // next in linked list

            public Slot(String name, int indexOrHash, int attributes)
            {
                this.name = name;
                this.indexOrHash = indexOrHash;
                this.attributes = (short)attributes;
            }

            public int getAttributes()
            {
                return attributes;
            }

            public void setAttributes(int value)
            {
                checkValidAttributes(value);
                attributes = (short)value;
            }

            public void checkNotReadonly()
            {
                if ((attributes & READONLY) != 0)
                {
                    String str = (name != null ? name
                                  : indexOrHash.ToString());
                    throw Context.reportRuntimeError1("msg.modify.readonly", str);
                }
            }
        }// end for Slot

        /// <summary>
        /// 验证属性
        /// </summary>
        /// <param name="attributes"></param>
        public static void checkValidAttributes(int attributes)
        {
            int mask = READONLY | DONTENUM | PERMANENT | UNINITIALIZED_CONST;
            if ((attributes & ~mask) != 0)
            {
                throw new ArgumentException(attributes.ToString());
            }
        }

        public class GetterSlot : Slot
        {
            public Object getter;
            public Object setter;

            public GetterSlot(String name, int indexOrHash, int attributes)
                : base(name, indexOrHash, attributes)
            {
            }
        }//GetterSlot

        private static bool IsLoad = false;
        public ScriptableObject()
        {
            if (!IsLoad)
            {
                REMOVED.wasDeleted = true;
            }
            IsLoad = true;
        }

        public ScriptableObject(Scriptable scope, Scriptable prototype)
            : this()
        {
            if (scope == null)
                throw new ArgumentException();

            parentScopeObject = scope;
            prototypeObject = prototype;
        }

        /// <summary>
        /// 得到本地域
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static Scriptable getTopLevelScope(Scriptable obj)
        {
            for (; ; )
            {
                Scriptable parent = obj.getParentScope();
                if (parent == null)
                {
                    return obj;
                }
                obj = parent;
            }
        }

        private Slot getSlot(String name, int index, int accessType)
        {
            Slot slot;

            // Query last access cache and check that it was not deleted.


            slot = lastAccess;
            if (name != null)
            {
                if (name != slot.name)//如果名字不为空，且Slot的名字与当前最后一个Slot的名字相同
                    goto lastAccessCheck;
                // No String.equals here as successful slot search update
                // name object with fresh reference of the same string.
            }
            else
            {
                if (slot.name != null || index != slot.indexOrHash)
                    goto lastAccessCheck;
            }

            if (slot.wasDeleted)
                goto lastAccessCheck;

            if (accessType == SLOT_MODIFY_GETTER_SETTER &&
                !(slot is GetterSlot))
                goto lastAccessCheck;

            return slot;

        lastAccessCheck: ;
            slot = accessSlot(name, index, accessType);
            if (slot != null)
            {
                // 更新Slot的缓存
                lastAccess = slot;
            }
            return slot;
        }

        /// <summary>
        /// 得到属性
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public virtual int getAttributes(int index)
        {
            return findAttributeSlot(null, index, SLOT_QUERY).getAttributes();
        }
        /// <summary>
        /// 得到属性
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public virtual int getAttributes(String name)
        {
            return findAttributeSlot(name, 0, SLOT_QUERY).getAttributes();
        }

        /// <summary>
        /// 访问Slot
        /// </summary>
        /// <param name="name"></param>
        /// <param name="index"></param>
        /// <param name="accessType">访问类型</param>
        /// <returns></returns>
        private Slot accessSlot(String name, int index, int accessType)
        {
            int indexOrHash = (name != null ? name.GetHashCode() : index);

            if (accessType == SLOT_QUERY ||
                accessType == SLOT_MODIFY ||
                accessType == SLOT_MODIFY_CONST ||
                accessType == SLOT_MODIFY_GETTER_SETTER)
            {
                // Check the hashtable without using synchronization

                Slot[] slotsLocalRef = slots; // Get stable local reference
                if (slotsLocalRef == null)
                {
                    if (accessType == SLOT_QUERY)
                        return null;//不是查询Slot
                }
                else
                {
                    int tableSize = slotsLocalRef.Length;
                    int slotIndex = getSlotIndex(tableSize, indexOrHash);
                    Slot slot = slotsLocalRef[slotIndex];
                    while (slot != null)
                    {
                        String sname = slot.name;
                        if (sname != null)
                        {
                            if (sname == name)
                                break;
                            if (name != null && indexOrHash == slot.indexOrHash)
                            {
                                if (name == sname)
                                {
                                    // This will avoid calling String.equals when
                                    // slot is accessed with same string object
                                    // next time.
                                    slot.name = name;
                                    break;
                                }
                            }
                        }
                        else if (name == null &&
                                   indexOrHash == slot.indexOrHash)
                        {
                            break;
                        }
                        slot = slot.next;
                    }
                    if (accessType == SLOT_QUERY)
                    {
                        return slot;
                    }
                    else if (accessType == SLOT_MODIFY)
                    {
                        if (slot != null)
                            return slot;
                    }
                    else if (accessType == SLOT_MODIFY_GETTER_SETTER)
                    {
                        if (slot is GetterSlot)
                            return slot;
                    }
                    else if (accessType == SLOT_MODIFY_CONST)
                    {
                        if (slot != null)
                            return slot;
                    }
                }

                // 一个新的 slot 已经被插入或者通过GetterSlot来替换
                // 

                lock (this)
                {
                    //如果另一个线程被触发，会刷新本地引用
                    slotsLocalRef = slots;
                    int insertPos;
                    if (count == 0)
                    {
                        // 如果没有就创建新的slots
                        slotsLocalRef = new Slot[5];
                        slots = slotsLocalRef;
                        insertPos = getSlotIndex(slotsLocalRef.Length, indexOrHash);
                    }
                    else
                    {
                        int tableSize = slotsLocalRef.Length;
                        insertPos = getSlotIndex(tableSize, indexOrHash);
                        Slot prev = slotsLocalRef[insertPos];
                        Slot slot = prev;
                        while (slot != null)
                        {
                            if (slot.indexOrHash == indexOrHash &&
                                (slot.name == name ||
                                 (name != null && name == slot.name)))
                            {
                                break;
                            }
                            prev = slot;
                            slot = slot.next;
                        }

                        if (slot != null)
                        {
                            // Another thread just added a slot with same
                            // name/index before this one entered synchronized
                            // block. This is a race in application code and
                            // probably indicates bug there. But for the hashtable
                            // implementation it is harmless with the only
                            // complication is the need to replace the added slot
                            // if we need GetterSlot and the old one is not.
                            if (accessType == SLOT_MODIFY_GETTER_SETTER &&
                                !(slot is GetterSlot))
                            {
                                GetterSlot newSlot = new GetterSlot(name,
                                        indexOrHash, slot.getAttributes());
                                newSlot.value = slot.value;
                                newSlot.next = slot.next;
                                // add new slot to linked list
                                if (lastAdded != null)
                                    lastAdded.orderedNext = newSlot;
                                if (firstAdded == null)
                                    firstAdded = newSlot;
                                lastAdded = newSlot;
                                // add new slot to hash table
                                if (prev == slot)
                                {
                                    slotsLocalRef[insertPos] = newSlot;
                                }
                                else
                                {
                                    prev.next = newSlot;
                                }
                                // other housekeeping
                                slot.wasDeleted = true;
                                slot.value = null;
                                slot.name = null;
                                if (slot == lastAccess)
                                {
                                    lastAccess = REMOVED;
                                }
                                slot = newSlot;
                            }
                            else if (accessType == SLOT_MODIFY_CONST)
                            {
                                return null;
                            }
                            return slot;
                        }

                        // Check if the table is not too full before inserting.
                        if (4 * (count + 1) > 3 * slotsLocalRef.Length)
                        {
                            slotsLocalRef = new Slot[slotsLocalRef.Length * 2 + 1];
                            copyTable(slots, slotsLocalRef, count);
                            slots = slotsLocalRef;
                            insertPos = getSlotIndex(slotsLocalRef.Length,
                                    indexOrHash);
                        }
                    }

                    Slot newSlot1 = (accessType == SLOT_MODIFY_GETTER_SETTER
                                    ? new GetterSlot(name, indexOrHash, 0)
                                    : new Slot(name, indexOrHash, 0));
                    if (accessType == SLOT_MODIFY_CONST)
                        newSlot1.setAttributes(CONST);
                    ++count;
                    // 加入新的Slot到链表
                    if (lastAdded != null)//如果表头不为空，就加入下一个命令中
                        lastAdded.orderedNext = newSlot1;
                    if (firstAdded == null)//如果表头不空，就加入新创建的这个节点
                        firstAdded = newSlot1;
                    lastAdded = newSlot1;
                    // 加入新的Slot到表然后返回
                    addKnownAbsentSlot(slotsLocalRef, newSlot1, insertPos);
                    return newSlot1;
                }

            }
            else if (accessType == SLOT_REMOVE)
            {
                lock (this)
                {
                    Slot[] slotsLocalRef = slots;
                    if (count != 0)
                    {
                        int tableSize = slots.Length;
                        int slotIndex = getSlotIndex(tableSize, indexOrHash);
                        Slot prev = slotsLocalRef[slotIndex];
                        Slot slot = prev;
                        while (slot != null)
                        {
                            if (slot.indexOrHash == indexOrHash &&
                                (slot.name == name ||
                                 (name != null && name == slot.name)))
                            {
                                break;
                            }
                            prev = slot;
                            slot = slot.next;
                        }
                        if (slot != null && (slot.getAttributes() & PERMANENT) == 0)
                        {
                            count--;
                            // remove slot from hash table
                            if (prev == slot)
                            {
                                slotsLocalRef[slotIndex] = slot.next;
                            }
                            else
                            {
                                prev.next = slot.next;
                            }
                            // Mark the slot as removed. It is still referenced
                            // from the order-added linked list, but will be
                            // cleaned up later
                            slot.wasDeleted = true;
                            slot.value = null;
                            slot.name = null;
                            if (slot == lastAccess)
                            {
                                lastAccess = REMOVED;
                            }
                        }
                    }
                }
                return null;

            }
            else
            {
                throw Kit.codeBug();
            }
        }
        private static int getSlotIndex(int tableSize, int indexOrHash)
        {
            return (indexOrHash & 0x7fffffff) % tableSize;
        }

        // Must be inside synchronized (this)
        private static void copyTable(Slot[] slots, Slot[] newSlots, int count)
        {
            if (count == 0) throw Kit.codeBug();

            int tableSize = newSlots.Length;
            int i = slots.Length;
            for (; ; )
            {
                --i;
                Slot slot = slots[i];
                while (slot != null)
                {
                    int insertPos = getSlotIndex(tableSize, slot.indexOrHash);
                    Slot next = slot.next;
                    addKnownAbsentSlot(newSlots, slot, insertPos);
                    slot.next = null;
                    slot = next;
                    if (--count == 0)
                        return;
                }
            }
        }

        private static void addKnownAbsentSlot(Slot[] slots, Slot slot,
                                           int insertPos)
        {
            if (slots[insertPos] == null)
            {
                slots[insertPos] = slot;
            }
            else
            {
                Slot prev = slots[insertPos];
                while (prev.next != null)
                {
                    prev = prev.next;
                }
                prev.next = slot;
            }
        }

        public void addLazilyInitializedValue(String name, int index,
                                    LazilyLoadedCtor init, int attributes)
        {
            if (name != null && index != 0)
                throw new Exception(name);
            checkNotSealed(name, index);
            GetterSlot gslot = (GetterSlot)getSlot(name, index,
                                                   SLOT_MODIFY_GETTER_SETTER);
            gslot.setAttributes(attributes);
            gslot.getter = null;
            gslot.setter = null;
            gslot.value = init;
        }
        public bool isSealed()
        {
            return count < 0;
        }
        private void checkNotSealed(String name, int index)
        {
            if (!isSealed())
                return;

            String str = (name != null) ? name : index.ToString();
            throw Context.reportRuntimeError1("msg.modify.sealed", str);
        }


        private bool putImpl(String name, int index, Scriptable start,
                            Object value, int constFlag)
        {
            Slot slot;
            if (this != start)
            {
                slot = getSlot(name, index, SLOT_QUERY);
                if (slot == null)
                {
                    return false;
                }
            }
            else
            {
                checkNotSealed(name, index);//是否被封闭
                // either const hoisted declaration or initialization 定义或者初始化
                if (constFlag != EMPTY)
                {
                    slot = getSlot(name, index, SLOT_MODIFY_CONST);
                    int attr = slot.getAttributes();
                    if ((attr & READONLY) == 0)
                        throw Context.reportRuntimeError1("msg.var.redecl", name);
                    if ((attr & UNINITIALIZED_CONST) != 0)
                    {
                        slot.value = value;
                        // clear the bit on const initialization
                        if (constFlag != UNINITIALIZED_CONST)
                            slot.setAttributes(attr & ~UNINITIALIZED_CONST);
                    }
                    return true;
                }
                slot = getSlot(name, index, SLOT_MODIFY);
            }
            if ((slot.getAttributes() & READONLY) != 0)
                return true;
            if (slot is GetterSlot)
            {
                Object setterObj = ((GetterSlot)slot).setter;
                if (setterObj == null)
                {
                    if (((GetterSlot)slot).getter != null)
                    {
                        // Based on TC39 ES3.1 Draft of 9-Feb-2009, 8.12.4, step 2,
                        // we should throw a TypeError in this case.
                        throw ScriptRuntime.typeError1("msg.set.prop.no.setter", name);
                    }
                }
                else
                {
                    Context cx = Context.getContext();
                    if (setterObj is MemberBox)
                    {
                        MemberBox nativeSetter = (MemberBox)setterObj;
                        Type[] pTypes = nativeSetter.argTypes;
                        // XXX: cache tag since it is already calculated in
                        // defineProperty ?
                        Type valueType = pTypes[pTypes.Length - 1];
                        int tag = FunctionObject.getTypeTag(valueType);
                        Object actualArg = FunctionObject.convertArg(cx, start,
                                                                     value, tag);
                        Object setterThis;
                        Object[] args;
                        if (nativeSetter.delegateTo == null)
                        {
                            setterThis = start;
                            args = new Object[] { actualArg };
                        }
                        else
                        {
                            setterThis = nativeSetter.delegateTo;
                            args = new Object[] { start, actualArg };
                        }
                        nativeSetter.invoke(setterThis, args);
                    }
                    else
                    {
                        Function f = (Function)setterObj;
                        f.call(cx, f.getParentScope(), start,
                               new Object[] { value });
                    }
                    return true;
                }
            }
            if (this == start)
            {
                slot.value = value;
                return true;
            }
            else
            {
                return false;
            }
        }

        public void sealObject()
        {
            if (count >= 0)
            {
                // Make sure all LazilyLoadedCtors are initialized before sealing.
                Slot slot = firstAdded;
                while (slot != null)
                {
                    if (slot.value is LazilyLoadedCtor)
                    {
                        LazilyLoadedCtor initializer = (LazilyLoadedCtor)slot.value;
                        try
                        {
                            initializer.init();
                        }
                        finally
                        {
                            slot.value = initializer.getValue();
                        }
                    }
                    slot = slot.orderedNext;
                }
                count = ~count;
            }
        }


        public static Object getTopScopeValue(Scriptable scope, Object key)
        {
            scope = ScriptableObject.getTopLevelScope(scope);
            for (; ; )
            {
                if (scope is ScriptableObject)
                {
                    ScriptableObject so = (ScriptableObject)scope;
                    Object value = so.getAssociatedValue(key);
                    if (value != null)
                    {
                        return value;
                    }
                }
                scope = scope.getPrototype();
                if (scope == null)
                {
                    return null;
                }
            }
        }

        public Object getAssociatedValue(Object key)
        {
            IDictionary<Object, Object> h = associatedValues;
            if (h == null)
                return null;
            return h[key];
        }


        /// <summary>
        /// 仿效SpiderMonkey的（和Firefox）功能允许
        /// 自定义对象，以避免正常“的目标检测检测”
        /// 代码模式。 参看 See https://bugzilla.mozilla.org/show_bug.cgi?id=412247.
        /// </summary>
        /// <returns></returns>
        public bool avoidObjectDetection()
        {
            return false;
        }

        public virtual Object equivalentValues(Object value)
        {
            return (this == value) ? true : ScriptableObject.NOT_FOUND;
        }

        public Object associateValue(Object key, Object value)
        {
            if (value == null) throw new ArgumentException();
            IDictionary<Object, Object> h = associatedValues;
            if (h == null)
            {
                h = associatedValues;
                if (h == null)
                {
                    h = new Dictionary<Object, Object>();
                    associatedValues = h;
                }
            }
            return Kit.initHash(h, key, value);
        }

        /// <summary>
        /// 设置属性到一个对象或者一个属性到prototype链
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="name"></param>
        /// <param name="value"></param>
        public static void putConstProperty(Scriptable obj, String name, Object value)
        {
            Scriptable base1 = getBase(obj, name);
            if (base1 == null)
                base1 = obj;
            if (base1 is ConstProperties)
                ((ConstProperties)base1).putConst(name, obj, value);
        }

        public static BaseFunction buildClassCtor(Scriptable scope, Type clazz,
            bool sealed1,
            bool mapInheritance)
        {
            XMLLibImpl.init(Context.getContext(), scope, sealed1);
            return null;
        }

        private Object getImpl(String name, int index, Scriptable start)
        {
            Slot slot = getSlot(name, index, SLOT_QUERY);
            if (slot == null)
            {
                return ScriptableObject.NOT_FOUND;
            }
            if (!(slot is GetterSlot))
            {
                return slot.value;
            }
            Object getterObj = ((GetterSlot)slot).getter;
            if (getterObj != null)
            {
                if (getterObj is MemberBox)
                {
                    MemberBox nativeGetter = (MemberBox)getterObj;
                    Object getterThis;
                    Object[] args;
                    if (nativeGetter.delegateTo == null)
                    {
                        getterThis = start;
                        args = ScriptRuntime.emptyArgs;
                    }
                    else
                    {
                        getterThis = nativeGetter.delegateTo;
                        args = new Object[] { start };
                    }
                    return nativeGetter.invoke(getterThis, args);
                }
                else
                {
                    Function f = (Function)getterObj;
                    Context cx = Context.getContext();
                    return f.call(cx, f.getParentScope(), start,
                                  ScriptRuntime.emptyArgs);
                }
            }
            Object value = slot.value;
            if (value is LazilyLoadedCtor)
            {
                LazilyLoadedCtor initializer = (LazilyLoadedCtor)value;
                try
                {
                    initializer.init();
                }
                finally
                {
                    value = initializer.getValue();
                    slot.value = value;
                }
            }
            return value;
        }


        private Slot findAttributeSlot(String name, int index, int accessType)
        {
            Slot slot = getSlot(name, index, accessType);
            if (slot == null)
            {
                String str = (name != null ? name : index.ToString());
                throw Context.reportRuntimeError1("msg.prop.not.found", str);
            }
            return slot;
        }


        public bool isGetterOrSetter(String name, int index, bool setter)
        {
            Slot slot = getSlot(name, index, SLOT_QUERY);
            if (slot is GetterSlot)
            {
                if (setter && ((GetterSlot)slot).setter != null) return true;
                if (!setter && ((GetterSlot)slot).getter != null) return true;
            }
            return false;
        }


        public static void defineConstProperty(Scriptable destination,
                                           String propertyName)
        {
            if (destination is ConstProperties)
            {
                ConstProperties cp = (ConstProperties)destination;
                cp.defineConst(propertyName, destination);
            }
            else
                defineProperty(destination, propertyName, Undefined.instance, CONST);
        }

        public static void redefineProperty(Scriptable obj, String name,
                                        bool isConst)
        {
            Scriptable base1 = getBase(obj, name);
            if (base1 == null)
                return;
            if (base1 is ConstProperties)
            {
                ConstProperties cp = (ConstProperties)base1;

                if (cp.isConst(name))
                    throw Context.reportRuntimeError1("msg.const.redecl", name);
            }
            if (isConst)
                throw Context.reportRuntimeError1("msg.var.redecl", name);
        }

    }//end scripttableObject

}
